﻿//  Copyright © Decebal Mihailescu 2013-2014
//  All rights reserved. This source code and binaries are distributed under 
//  Microsoft Public PatternLicense (Ms-PL)
//  The latest code is available at http://VSDesignPatterns.codeplex.com

//  Redistributions of source code must retain the above copyright notice, 
//  this list of conditions and the following disclaimer. 
//  Redistributions in binary form must reproduce the above copyright notice, 
//  this list of conditions and the following disclaimer in the documentation 
//  and/or other materials provided with the distribution. 

//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER 
//  REMAINS UNCHANGED.

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.IO;
using System.Diagnostics;
using System.Xml;
using Microsoft.Win32;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Reflection;
using System.Globalization;

namespace DesignPatterns
{
    using DesignPatternsWizard;
    using Common;

    /*
    http://www.mztools.com/articles/2005/MZ2005002.aspx
    delete addin http://www.mztools.com/articles/2006/MZ2006018.aspx
    reset add-in devenv.exe /resetaddin DesignPatterns.Connect  /Command File.Exit http://www.mztools.com/articles/2006/MZ2006014.aspx
    */
    //#pragma warning disable 1591

    [RunInstaller(true)]
    public partial class AddInInstaller : Installer
    {
        private const string ExtNameSpace = "http://schemas.microsoft.com/AutomationExtensibility";
        string _addInTargetFolder;
        static string _vsPath;

        public AddInInstaller()
        {
            string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "DesignPatternsInstall_" + Utils.VSVersion + ".log");
            Logging.Instance.Init(3, 1024 * 1024, logPath);
            InitializeComponent();
            InitLogging();
        }
        private static bool Is64bitProcess { get { return Marshal.SizeOf(IntPtr.Zero) == 8; } }
        private void InitLogging()
        {

            Logging.Instance.Log(3, "DesignPatterns Install version {5} initialized at {0}, Win ver = {1}, Crt dir: '{2}', CLR version ={3}, Is64BitProcess = {4}",
                DateTime.Now, Environment.OSVersion.Version, Environment.CurrentDirectory, RuntimeEnvironment.GetSystemVersion(), Is64bitProcess,
                Assembly.GetExecutingAssembly().GetName().Version);
            try
            {
                _vsPath = Utils.VSPath;
                _addInTargetFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                    string.Format(@"Visual Studio {0}\Addins", Utils.VSYear));
                //clean garbage 
                string[] files = Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
             "*-*-*DesignPatterns*_*.log", SearchOption.TopDirectoryOnly);
                foreach (string f in files)
                    try
                    {
                        File.Delete(f);
                    }
                    catch { }
            }
            catch (Exception ex)
            {

                Logging.Instance.Log(1, "InitLogging exception " + ex.Message);
            }
        }

        /// <summary>
        /// Overrides Installer.Install, which will be executed during install process.
        /// </summary>
        /// <param SearchProviderName="savedState">The saved state.</param>
        public override void Install(IDictionary savedState)
        {

            // Uncomment the following line, recompile the setup
            // project and run the setup executable if you want
            // to debug into this custom action.
#if DEBUG
            //  Debugger.Break();
#endif
            try
            {
                base.Install(savedState);

                Logging.Instance.Log(3, "Install started, VSPath ={0} addInTargetFolder ={1}", _vsPath ?? "none", _addInTargetFolder ?? "none");
                if (!File.Exists(_vsPath))
                {
                    if (Utils.ShowModalDlg("Can't find Visual Studio " + Utils.VSYear + " IDE at\r\n" + _vsPath + "\r\nDo you want to continue anyway?"
                          , "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                    {
                        Logging.Instance.Log(3, "Install: User cancels");
                        throw new InstallException("User cancelled");
                    }
                }
                else
                {
                    try
                    {
                        Assembly targetAssembly = Assembly.GetExecutingAssembly();
                        Version v = targetAssembly.GetName().Version;
                        float assemblyVer = float.Parse(string.Format("{0}.{1}", v.Major, v.Minor), CultureInfo.InvariantCulture.NumberFormat);
                        string downloadUrl;
                        float onlineVersion;
                        if (v.Minor % 2 == 0)
                        {
                            if (Utils.NewOnlineVersionExists(out downloadUrl, out onlineVersion))
                            {// show only for minor upgrades
                                //if (onlineVersion > assemblyVer && Math.Floor(onlineVersion) == Math.Floor(assemblyVer))
                                // shows only for even online versions
                                if (onlineVersion > assemblyVer && (Convert.ToInt32(onlineVersion * 100) % 2 == 0))
                                {
                                    if (Utils.ShowModalDlg("Version " + onlineVersion.ToString(CultureInfo.InvariantCulture) + " is available online.\r\nWould you like to download and install it instead?", "A newer version than the current " + assemblyVer + " is available", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                                    {
                                        System.Diagnostics.Process.Start(string.IsNullOrEmpty(downloadUrl) ? Utils.BaseURL : downloadUrl);
                                        Logging.Instance.Log(3, "Install: user cancellig install to download from " + downloadUrl);
                                        throw new InstallException("User cancelled");
                                    }
                                }
                            }
                            Logging.Instance.Log(3, "Install: online version checked, continuing...");
                        }
                        else//od minor version
                        {
                            Utils.LastOnlineCheck = DateTime.Today;
                            Utils.LastOnlineVer = string.Format("{0}.{1}", v.Major, v.Minor);
                        }
                    }
                    catch (InstallException ex)
                    {
                        Logging.Instance.Log(1, "InstallException thrown in Install: " + ex.ToString());
                        throw;
                    }
                    catch (Exception ex)
                    {
                        Logging.Instance.Log(1, "Exception thrown in Install: " + ex.ToString());
                        throw new InstallException("Install failed because " + ex.Message, ex);
                    }
                    finally
                    {
                    }

#if !DEBUG
                    if (Process.GetProcessesByName("devenv").Length > 0)
                        Utils.ShowModalDlg("It is recommended that you close\r\nVisual Studio before continuing!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
#endif
                }
                string assemblyFolder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                try
                {
                    Logging.Instance.Log(3, "Install: installing Template Library in folder " + assemblyFolder);
                    // Parameters required to pass in from installer
                    string msiDirPath = this.Context.Parameters["MsiSrcDir"];
                    //InstallTemplateLibrary(savedState, assemblyFolder, msiDirPath);
                    Logging.Instance.Log(3, "Install: renaming add ins");
                    RenameDesignPatternsAddIns();
                    Logging.Instance.Log(3, "Install: changing the control file...");
                    SetAddInControlFile(savedState, assemblyFolder);
                    //save providers
                    //using (RegistryKey destinationKey = Registry.CurrentUser.CreateSubKey(Utils.PlugInsHive + "BK", RegistryKeyPermissionCheck.ReadWriteSubTree))
                    //{
                    //    //Open the sourceKey we are copying from
                    //    using (RegistryKey sourceKey = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive, false))
                    //    {
                    //        bool bRes = sourceKey.RecursiveCopyTo(destinationKey, ((_) => true), ((_) => true));
                    //    }
                    //}
                    //remove BK key if there
                    using (RegistryKey sourceKey = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive + "BK", true))
                    {
                        if (sourceKey != null)
                            sourceKey.DeleteSubKeyTree("");
                    }
                    //clean up all my specific providers
                    using (RegistryKey destinationKey = Registry.CurrentUser.CreateSubKey(Utils.PlugInsHive + "BK", RegistryKeyPermissionCheck.ReadWriteSubTree))
                    {
                        //Open the sourceKey we are copying from
                        using (RegistryKey sourceKey = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive, false))
                        {
                            const string vsYear = "VS" + Utils.VSYear;
                            bool bRes = sourceKey.RecursiveCopyTo(destinationKey, ((_) => true), ((s) => s != vsYear));
                        }
                    }
                    using (RegistryKey sourceKey = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive, true))
                    {
                        if (sourceKey != null)
                            sourceKey.DeleteSubKeyTree("");
                    }


                    //foreach (var lp in MEFController.Instance.GetLPInstatntiated(null))//.Distinct().Where(lp => lp.Key.LicenseType == PatternLicense.Free))
                    //{
                    //    lp.Value.RegisterLP(true, savedState, msiDirPath, null);

                    //}


                    NativeWindow nativeWnd = new NativeWindow();
                    try
                    {
                        IntPtr hWnd = (from p in Process.GetProcessesByName("msiexec")
                                       where (p.MainWindowHandle != IntPtr.Zero && (p.MainWindowTitle == "DesignPatterns for VS " + Utils.VSYear || p.MainWindowTitle == "Windows Installer"))
                                       select p.MainWindowHandle).SingleOrDefault();
                        //nativeWnd = NativeWindow.FromHandle(hWnd); //does not work

                        //IntPtr hwnd = Process.GetCurrentProcess().MainWindowHandle; is always 0
                        if (hWnd != IntPtr.Zero)
                            nativeWnd.AssignHandle(hWnd);
                        //ConfigForm called it first, try again just fot the trial to get evaluation license
                        foreach (var lp in MEFController.Instance.GetLPInstatntiated(null).Distinct())//.Where(lp => lp.Key.LicenseType == PatternLicense.Trial))
                        {
                            lp.Value.RegisterLP(true, savedState, msiDirPath, (hWnd == IntPtr.Zero) ? null : nativeWnd);

                        }


                    }
                    catch (Exception ex)
                    {
                        Logging.Instance.Log(1, "Exception thrown in Commit getting the window handle: " + ex.ToString());
                    }
                    finally { nativeWnd.ReleaseHandle(); }

                }
                catch (InstallException ex)
                {
                    Logging.Instance.Log(1, ex.Message);
                    Utils.ShowModalDlg(ex.Message, "InstallException thrown while installing", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    throw;
                }
                catch (Exception ex)
                {
                    Logging.Instance.Log(1, ex.Message);
                    Utils.ShowModalDlg(ex.Message, "Exception thrown while installing", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    throw new InstallException(ex.Message, ex);
                }
                finally
                {

                }
                Logging.Instance.Log(3, "Install: completed");
            }
            catch (InstallException ex)
            {
                Logging.Instance.Log(1, "Final InstallException thrown in Install: " + ex.ToString());
                throw;
            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "Final Exception thrown in Install: " + ex.ToString());
                throw new InstallException("Install failed because " + ex.Message, ex);
            }
        }

        private void SetAddInControlFile(IDictionary savedState, string assemblyFolder)
        {
            // Parameters required to pass in from installer's custom actions, more see http://msdn.microsoft.com/en-us/library/aa371857.aspx
            string msiDirPath = this.Context.Parameters["MsiSrcDir"];
            string productName = this.Context.Parameters["ProductName"];
            string assemblyName = this.Context.Parameters["AssemblyName"];

            // Setup .addin prex and assembly prex
            string addinControlFileName = assemblyName + ".Addin";

            Logging.Instance.Log(3, "Install: productName = {0}, VSersion = {1}, addinControlFileName = {2}, assemblyFolder = {3}",
                productName, Utils.VSVersion, addinControlFileName, assemblyFolder);
            string addinAssemblyFileName = assemblyName + ".dll";
            //change and deploy the .Addin file
            string sourceFile = Path.Combine(assemblyFolder, addinControlFileName);
            // remove readonly file attr
            FileAttributes fa = File.GetAttributes(sourceFile);
            if ((uint)(fa & FileAttributes.ReadOnly) != 0)
            {
                fa ^= FileAttributes.ReadOnly;// remove read only
                File.SetAttributes(sourceFile, fa);
            }
            Logging.Instance.Log(3, "Install: loading {0}", sourceFile);

            XDocument doc = XDocument.Load(sourceFile);
            XmlReader reader = doc.CreateReader();
            XmlNamespaceManager xnm = new XmlNamespaceManager(reader.NameTable);
            xnm.AddNamespace("def", ExtNameSpace);

            // Update Addin/Assembly node
            XElement node = doc.Root.XPathSelectElement("/def:Extensibility/def:Addin/def:Assembly", xnm);
            if (node != null)
            {
                node.Value = Path.Combine(assemblyFolder, addinAssemblyFileName);
                Logging.Instance.Log(3, "Install: changed assembly node");
            }
            // Update VS version node
            node = doc.Root.XPathSelectElement("/def:Extensibility/def:HostApplication/def:Version", xnm);
            if (node != null)
            {
                node.Value = Utils.VSVersion;
                Logging.Instance.Log(3, "Install: setted the correct VS version");
            }
            // Update ToolsOptionsPage/Assembly node
            node = doc.Root.XPathSelectElement("/def:Extensibility/def:ToolsOptionsPage/def:Category/def:SubCategory/def:Assembly", xnm);
            if (node != null)
            {
                node.Value = Path.Combine(assemblyFolder, addinAssemblyFileName);
                Logging.Instance.Log(3, "Install: changed subcategory node");
            }

            reader.Close();
            doc.Save(sourceFile);
            Logging.Instance.Log(3, "Install: saved {0}", sourceFile);

            string targetFile = Path.Combine(_addInTargetFolder, assemblyName + ".Addin");
            File.Copy(sourceFile, targetFile, true);
            Logging.Instance.Log(3, "Install: copied '{0}' to '{1}'", sourceFile, targetFile);
            // Save AddinPath to be used in Uninstall or Rollback
            if (savedState["AddinPath"] == null)
                savedState.Add("AddinPath", targetFile);
        }

        private void RenameDesignPatternsAddIns()
        {
            DirectoryInfo dirInfo = new DirectoryInfo(_addInTargetFolder);
            if (!dirInfo.Exists)
            {
                Logging.Instance.Log(3, "Install: creating dir {0}", _addInTargetFolder);
                dirInfo.Create();
            }
            //rename other addins to remove conflicts
            FileInfo[] files = dirInfo.GetFiles();
            foreach (FileInfo fi in files)
            {
                try
                {
                    if (fi.Extension.EndsWith("addin", StringComparison.InvariantCultureIgnoreCase) && fi.Name.ToUpperInvariant().Contains("DESIGNPATTERNS"))
                    {
                        string bakFile = fi.FullName + "_";
                        if (File.Exists(bakFile))
                            File.Delete(bakFile);
                        fi.MoveTo(bakFile);
                    }
                }
                catch (Exception ex)
                {

                    Logging.Instance.Log(2, "Install: Exception while moving .addin files {0}", ex.Message);
                }

            }
        }


        /// <summary>
        /// Overrides Installer.Rollback, which will be executed during rollback process.
        /// </summary>
        /// <param SearchProviderName="savedState">The saved state.</param>
        public override void Rollback(IDictionary savedState)
        {
#if DEBUG
            //Debugger.Break();
#endif


            try
            {
                base.Rollback(savedState);
                string fileName = (string)savedState["AddinPath"];
                Logging.Instance.Log(3, "RollBack: deleting " + fileName);
                if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
                {
                    File.Delete(fileName);
                }
                //clean up all my vs2008 providers
                using (RegistryKey destinationKey = Registry.CurrentUser.CreateSubKey(Utils.PlugInsHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    //Open the sourceKey we are copying from
                    using (RegistryKey sourceKey = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive + "BK", false))
                    {
                        const string vsYear = "VS" + Utils.VSYear;
                        bool bRes = sourceKey.RecursiveCopyTo(destinationKey, ((_) => true), ((s) => s != vsYear));
                    }
                }
                using (RegistryKey sourceKey = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive + "BK", true))
                {
                    if (sourceKey != null)
                        sourceKey.DeleteSubKeyTree("");
                }
            }
            catch (InstallException ex)
            {
                Logging.Instance.Log(1, "InstallException thrown in RollBack: " + ex.ToString());
                throw;
            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "Exception thrown in RollBack: " + ex.ToString());
                throw new InstallException("RollBack failed because " + ex.Message, ex);
            }
            Logging.Instance.Log(3, "Rollback: completed");
        }
        public override void Commit(IDictionary savedState)
        {
#if DEBUG
            //Debugger.Break();
#endif
            try
            {
                base.Commit(savedState);

                //just for other apps plugging in the design patterns
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(Common.Utils.AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    Version v = Assembly.GetExecutingAssembly().GetName().Version;
                    reg.SetValue("CurrentVersion", string.Format("{0}.{1}", v.Major, v.Minor), RegistryValueKind.String);
                }
                //set the locattions
                ConfigForm cfg = new ConfigForm(2);
                ShowModalForm(cfg);
                //bool res = VSResetAddIn(true);
                //restore the other providers
                using (RegistryKey destinationKey = Registry.CurrentUser.CreateSubKey(Utils.PlugInsHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    //Open the sourceKey we are copying from
                    using (RegistryKey sourceKey = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive + "BK", false))
                    {
                        const string vsYear = "VS" + Utils.VSYear;
                        bool bRes = sourceKey.RecursiveCopyTo(destinationKey, ((_) => true), ((s) => s != vsYear));
                    }
                }
                using (RegistryKey sourceKey = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive + "BK", true))
                {
                    if (sourceKey != null)
                        sourceKey.DeleteSubKeyTree("");
                }
                Logging.Instance.Log(2, "Commit: VSResetAddIn(true) returned '{0}'", VSResetAddIn(true));

                //string msiDirPath = this.Context.Parameters["MsiSrcDir"];
                //NativeWindow nativeWnd = new NativeWindow();
                //try
                //{
                //    IntPtr hWnd = (from p in Process.GetProcessesByName("msiexec")
                //                   where (p.MainWindowHandle != IntPtr.Zero && (p.MainWindowTitle == "DesignPatterns for VS " + Utils.VSYear || p.MainWindowTitle == "Windows Installer"))
                //                   select p.MainWindowHandle).SingleOrDefault();
                //    //nativeWnd = NativeWindow.FromHandle(hWnd); //does not work

                //    //IntPtr hwnd = Process.GetCurrentProcess().MainWindowHandle; is always 0
                //    if (hWnd != IntPtr.Zero)
                //        nativeWnd.AssignHandle(hWnd);
                //    //afret ConfigForm called it first, try again just fot the trial to get evaluation license
                //    foreach (var lp in MEFController.Instance.GetLPInstatntiated(null).Distinct().Where(lp => lp.Key.LicenseType == PatternLicense.Trial))
                //    {
                //        lp.Value.RegisterLP(true, savedState, msiDirPath, (hWnd == IntPtr.Zero) ? null : nativeWnd);

                //    }


                //}
                //catch (Exception ex)
                //{
                //    Logging.Instance.Log(1, "Exception thrown in Commit getting the window handle: " + ex.ToString());
                //}
                //finally { nativeWnd.ReleaseHandle(); }


            }
            catch (InstallException ex)
            {
                Logging.Instance.Log(1, "InstallException thrown in Commit: " + ex.ToString());
                throw;
            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "Exception thrown in Commit: " + ex.ToString());
                throw new InstallException("Commit failed because " + ex.Message, ex);
            }
            Logging.Instance.Log(3, "Commit: completed");
        }

        private static void ShowModalForm(Form cfg)
        {
            NativeWindow nativeWnd = new NativeWindow();
            try
            {
                IntPtr hWnd = (from p in Process.GetProcessesByName("msiexec")
                               where (p.MainWindowHandle != IntPtr.Zero && (p.MainWindowTitle == "DesignPatterns for VS " + Utils.VSYear || p.MainWindowTitle == "Windows Installer"))
                               select p.MainWindowHandle).SingleOrDefault();

                if (hWnd == IntPtr.Zero)
                    cfg.ShowDialog();
                else
                {
                    nativeWnd.AssignHandle(hWnd);

                    cfg.ShowDialog(nativeWnd);
                }

            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "Exception thrown in Commit " + ex.Message);
            }
            finally
            {
                nativeWnd.ReleaseHandle();
                cfg.Dispose();
            }
        }

        private bool VSResetAddIn(bool wait)
        {
            try
            {

                if (File.Exists(_vsPath))
                {//see http://visualstudiohacks.com/tips/when-resetaddin-doesnt-quite-cut-it/ 
                    //or http://social.msdn.microsoft.com/Forums/vstudio/en-US/5f2948f8-6e76-42bf-8685-aa9bfa4f60ef/vs2005-problems-with-devenvexe-resetaddin-addin-command-fileexit-
                    //start vs with no add-in and /resetaddin param, then close VS and start again
                    //use startup info & minimize window
                    //  ProcessStartInfo psi = new ProcessStartInfo(vsPath, "/resetaddin * /Command File.Exit");
                    string vsargs = "/resetaddin " + typeof(DesignPatterns.Connect).FullName + " /Command File.Exit";
                    Logging.Instance.Log(3, "VSResetAddIn: running: {0} {1}", _vsPath, vsargs);
                    ProcessStartInfo psi = new ProcessStartInfo(_vsPath, vsargs);
                    psi.WindowStyle = ProcessWindowStyle.Hidden;//change to minimized or hidden
                    Process p = Process.Start(psi);
                    //psi.WindowStyle = ProcessWindowStyle.Hidden;//original

                    if (wait)
                    {
                        bool completed = p.WaitForExit(30000);//wait up to 30 seconds
                        Logging.Instance.Log(2, "VSReset: WaitForExit returned " + completed.ToString());
                        return completed;
                    }

                    return true;
                }
                else
                {
                    Logging.Instance.Log(2, "VSResetAddIn: won't run inexistent '{0}'", _vsPath);
                    return false;
                }
            }
            catch (Exception ex1)
            {

                Logging.Instance.Log(2, "VSResetAddIn:Could not start '{0}' because {1}", _vsPath, ex1.Message);
                return false;
            }
        }
        /// <summary>
        /// Overrides Installer.Uninstall, which will be executed during uninstall process.
        /// </summary>
        /// <param SearchProviderName="savedState">The saved state.</param>
        public override void Uninstall(IDictionary savedState)
        {


#if DEBUG
            // Debugger.Break();
#endif
            try
            {


                base.Uninstall(savedState);
#if !DEBUG
                if (Process.GetProcessesByName("devenv").Length > 0)
                    Utils.ShowModalDlg("It is recommended that you close\r\nVisual Studio before continuing!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
#endif
                try
                {


                    foreach (var lp in MEFController.Instance.GetLPInstatntiated(null))
                    {
                        try
                        {
                            lp.Value.UnregisterLP(savedState);
                        }
                        catch (Exception ex)
                        {

                            Logging.Instance.Log(2, "Uninstall: exception thrown for UnregisterLP '{0}'", ex.Message);
                        }
                    }

                }
                catch (Exception ex)
                {
                    Logging.Instance.Log(2, "Uninstall: exception thrown '{0}'", ex.Message);

                }
                Registry.CurrentUser.DeleteSubKeyTree(Common.Utils.AppHive);
                DirectoryInfo dirInfo = new DirectoryInfo(_addInTargetFolder);
                //restore other addins 
                FileInfo[] files = dirInfo.GetFiles();
                foreach (FileInfo fi in files)
                {
                    try
                    {
                        if (fi.Extension.EndsWith("addin_", StringComparison.InvariantCultureIgnoreCase) && fi.Name.ToUpperInvariant().Contains("DESIGNPATTERNS"))
                        {
                            Logging.Instance.Log(3, "Uninstall: restoring '{0}'", fi.FullName);
                            fi.MoveTo(fi.FullName.Substring(0, fi.FullName.Length - 1));
                        }
                    }
                    catch (Exception ex)
                    {
                        Logging.Instance.Log(2, "Uninstall: exception thrown '{0}' for {1}", ex.Message, fi.FullName);

                    }
                }
                Logging.Instance.Log(2, "Uninstall: VSResetAddIn(true) returned '{0}'", VSResetAddIn(true));

            }
            catch (InstallException ex)
            {
                Logging.Instance.Log(1, "InstallException thrown in UnInstall: " + ex.ToString());
                throw;
            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "Exception thrown in UnInstall: " + ex.ToString());
                throw new InstallException("UnInstall failed because " + ex.Message, ex);
            }
            Logging.Instance.Log(3, "Uninstall: completed");
        }

    }
}
